

var TAG_SPRITE_MANAGER = 1;
var PTM_RATIO = 32.0;

/* b2 Var shortcut */
var b2Vec2 = Box2D.Common.Math.b2Vec2
		, b2BodyDef = Box2D.Dynamics.b2BodyDef
		, b2Body = Box2D.Dynamics.b2Body
		, b2FixtureDef = Box2D.Dynamics.b2FixtureDef
		, b2World = Box2D.Dynamics.b2World
		, b2ContactListener = Box2D.Dynamics.b2ContactListener
		, b2AABB = Box2D.Collision.b2AABB
        , b2CircleShape = Box2D.Collision.Shapes.b2CircleShape
		, b2PolygonShape = Box2D.Collision.Shapes.b2PolygonShape;



var b2DebugDrawLayer = cc.Node.extend({
	
	b2World:null,
	
	ctor:function(_world){
		this._super();
		
		this.b2World = _world;
		/*
		Box2D.Dynamics.b2DebugDraw.e_shapeBit = 0x0001;
        Box2D.Dynamics.b2DebugDraw.e_jointBit = 0x0002;
        Box2D.Dynamics.b2DebugDraw.e_aabbBit = 0x0004;
        Box2D.Dynamics.b2DebugDraw.e_pairBit = 0x0008;
        Box2D.Dynamics.b2DebugDraw.e_centerOfMassBit = 0x0010;
        Box2D.Dynamics.b2DebugDraw.e_controllerBit = 0x0020;
		*/
		
		this.b2World.SetDebugDraw( new Box2D.Dynamics.b2DebugDraw() );
		this.b2World.m_debugDraw.AppendFlags( Box2D.Dynamics.b2DebugDraw.e_shapeBit );
		this.b2World.m_debugDraw.m_drawScale = PTM_RATIO;
		this.b2World.m_debugDraw.SetAlpha(0.6);
		this.b2World.m_debugDraw.SetFillAlpha(0.6);
		
		this.b2World.m_debugDraw.SetSprite( cc.renderContext );
		
		var size = cc.Director.getInstance().getWinSize();
		//this.setPosition( cc.p(size.width / 2, size.height / 2) );
		this.setScaleY( -1 );
	},
	
	draw:function (ctx) {
		this.b2World.DrawDebugData();
	},
});


var TagList = {
  Common :      {value: 0, name: "Common"}, 
  Box:          {value: 1, name: "Box"}, 
  WaterSensor : {value: 2, name: "WaterSensor"},
  Circle :      {value: 3, name: "Circle"},
};

function b2AddOn (cocosData) {
    this.cocosData = cocosData;
    this.selectable = false;
    this.tag = TagList.Common;
};


function transformToB2World(cocosWorldP){
	var worldP = cocosWorldP;
	worldP = cc.Director.getInstance().convertToGL(worldP);
	var b2WorldPos = new b2Vec2( worldP.x / PTM_RATIO, worldP.y / PTM_RATIO );
	
	return b2WorldPos;
};

var _waterScene = cc.Layer.extend({
   
	_water:null,
	world:null,
	boxBatchNode:null,
	SelectedObject:null,
	b2MousePosition:null,
	waterSensorAABB:null,
	BoxInWaterList:[],
	
    init:function () {
        var selfPointer = this;
        //////////////////////////////
        // 1. super init first
        this._super();

		this.setTouchEnabled(true);
		this.setKeyboardEnabled(true);
		
		
		
		var size = cc.Director.getInstance().getWinSize();
		
		
		// physic
        this.world = new b2World(new b2Vec2(0, -10), false);
        this.world.SetContinuousPhysics(true);
		
		
		// physic debug draw
		var Debug = new b2DebugDrawLayer( this.world );
		Debug.setVisible(false);
		this.addChild( Debug , 255 );
		
		
		// contact response
		this.initContactListener();

		// world limit 
		this.initWorldBound();
		
		
		// background

        var background = cc.Sprite.create("res/back_waterScene.png");
        background.setPosition(cc.p(size.width / 2, size.height / 2));
        background.setVisible(true);
        background.setAnchorPoint(cc.p(0.5, 0.5));
        this.addChild(background, 0);

	
		//Menu (back button)
		var items = new Array();
		var p = cc.p( (-size.width/2) + 20 ,
					  (-size.height/2) + 30 );
		items[0] = createButton("Back");
		items[0].setPosition( p );
		items[0].setCallback(this,
            function () {
				myApp.GoToScene( new HelloWorldScene() );
            });
		var myMenu = cc.MenuCustom.create(items);
		this.addChild(myMenu);
		
		
		// glass
		var __this = this;
		this.initGlass( function(){
			
			// in the middle of the glass
		
			//Set up Box sprite
			var mgr = cc.SpriteBatchNode.create("res/Box.png", 10);
			__this.addChild(mgr);
			__this.boxBatchNode = mgr;
			__this.addNewSpriteWithCoords(cc.p(50,50));
			__this.addNewSpriteWithCoords(cc.p(80,200));
			__this.addNewSpriteWithCoords(cc.p(150,300));
			__this.addNewSpriteWithCoords(cc.p(40,450));
			__this.addNewSpriteWithCoords(cc.p(200,100));
			__this.addNewSpriteWithCoords(cc.p(180,380));
		});

		
		this.scheduleUpdate();
		
        return true;
    },
	
	addNewSpriteWithCoords:function (p) {
	
        var batch = this.boxBatchNode;

		var CCSize = 50;
		var b2Size = CCSize / PTM_RATIO;
		
        var sprite = cc.Sprite.createWithTexture(batch.getTexture(), cc.rect(0, 0, CCSize, CCSize));
        batch.addChild(sprite);
        sprite.setPosition(cc.p(p.x, p.y));

        // Define the dynamic body.
        //Set up a 1m squared box in the physics world
        var b2BodyDef = Box2D.Dynamics.b2BodyDef
            , b2Body = Box2D.Dynamics.b2Body
            , b2FixtureDef = Box2D.Dynamics.b2FixtureDef
            , b2PolygonShape = Box2D.Collision.Shapes.b2PolygonShape;

        var bodyDef = new b2BodyDef();
        bodyDef.type = b2Body.b2_dynamicBody;
        bodyDef.position.Set(p.x / PTM_RATIO, p.y / PTM_RATIO);
        bodyDef.userData = new b2AddOn(sprite);
        bodyDef.userData.selectable = true;
        bodyDef.userData.tag = TagList.Box;
        var body = this.world.CreateBody(bodyDef);

        // Define another box shape for our dynamic body.
        var dynamicBox = new b2PolygonShape();
        dynamicBox.SetAsBox(b2Size/2.0, b2Size/2.0);//These are mid points for our 'b2Size' meter box

        // Define the dynamic body fixture.
        var fixtureDef = new b2FixtureDef();
        fixtureDef.shape = dynamicBox;
        fixtureDef.density = 1.0;
        fixtureDef.friction = 0.3;
        body.CreateFixture(fixtureDef);
    },
	
	// init function
	initContactListener:function(){
		
		        var __this = this;
		var myContactListener = new b2ContactListener();
		myContactListener.BeginContact = function(contact){
			
            var b2AddOnA = contact.m_fixtureA.GetBody().GetUserData();
            var b2AddOnB = contact.m_fixtureB.GetBody().GetUserData();
            
            if( b2AddOnA.tag == TagList.Box &&
                b2AddOnB.tag == TagList.WaterSensor )
            {
                var contact_point = b2AddOnA.cocosData.getPosition();
                
                __this._water.Height += 10;
                __this._water.addForce(contact_point,-200);
            } 
		};
		myContactListener.EndContact = function(contact){
        
            var b2AddOnA = contact.m_fixtureA.GetBody().GetUserData();
            var b2AddOnB = contact.m_fixtureB.GetBody().GetUserData();
            
            if( b2AddOnA.tag == TagList.Box &&
                b2AddOnB.tag == TagList.WaterSensor )
            {
                var contact_point = b2AddOnA.cocosData.getPosition();
                
                __this._water.Height -= 10;
                __this._water.addForce(contact_point,200);
            }
		};
		this.world.SetContactListener(myContactListener);
	},
	initWorldBound:function(){
		var size = cc.Director.getInstance().getWinSize();
		
		var fixDef = new b2FixtureDef;
        fixDef.density = 1.0;
        fixDef.friction = 0.5;
        fixDef.restitution = 0.2;

        var bodyDef = new b2BodyDef;
        //create ground
        bodyDef.type = b2Body.b2_staticBody;
        fixDef.shape = new b2PolygonShape;
        fixDef.shape.SetAsBox(20, 2);
        // upper
        bodyDef.position.Set(10, size.height / PTM_RATIO + 1.8);
        bodyDef.userData = new b2AddOn();
        this.world.CreateBody(bodyDef).CreateFixture(fixDef);
        // bottom
        bodyDef.position.Set(10, -1.8);
        bodyDef.userData = new b2AddOn();
        this.world.CreateBody(bodyDef).CreateFixture(fixDef);
        fixDef.shape.SetAsBox(2, 14);
        // left
        bodyDef.position.Set(-1.8, 13);
        bodyDef.userData = new b2AddOn();
        this.world.CreateBody(bodyDef).CreateFixture(fixDef);
        // right
        bodyDef.position.Set(26.8, 13);
        bodyDef.userData = new b2AddOn();
        this.world.CreateBody(bodyDef).CreateFixture(fixDef);
	},
	initGlass:function( MiddleLayerCallback ){
		
		var size = cc.Director.getInstance().getWinSize();
		// glass def
		var glassX = 560.0;
		var glassY = -1.0;
		var glassW = 231.0;
		var glassH = 243.0;
		var glassBW = 231.0;
		var glassBH = 10.0;
		var glassLW = 17.0;
		var glassLH = 222.0;
		var glassRW = 17.0;
		var glassRH = 222.0;
		var glassRXOffset = 214.0;
		var glassSensorH = 100.0;
		
		
		// glass background
		var glass = cc.CustomSprite.create("res/glass_b.png");
        glass.setPosition(cc.p(glassX,glassY));
		this.addChild(glass);
		
		
		
		// callback object between background and front
		if( MiddleLayerCallback )
			MiddleLayerCallback();
		
		
		
		// water
		var water = new Water(glassW-4,100,12);
		water.setPosition(cc.p(glassX+2,glassY+3));
		this.addChild(water);
		this._water = water;
		
		
		// glass foreground
		var glass = cc.CustomSprite.create("res/glass.png");
        glass.setPosition(cc.p(glassX,glassY));
		this.addChild(glass);
			// glass physic
			var glassfixDef = new b2FixtureDef;
			glassfixDef.density = 1.0;
			glassfixDef.friction = 0.5;
			glassfixDef.restitution = 0.2;

			var glassBodyDef = new b2BodyDef;
			glassBodyDef.type = b2Body.b2_staticBody;
			// Bottom
			glassfixDef.shape = new b2PolygonShape;
			glassfixDef.shape.SetAsBox( (glassBW/2)/PTM_RATIO, (glassBH/2)/PTM_RATIO );
			glassBodyDef.position.Set( (glassX+(glassBW/2))/PTM_RATIO, (glassY+(glassBH/2))/PTM_RATIO);
            glassBodyDef.userData = new b2AddOn();
			this.world.CreateBody(glassBodyDef).CreateFixture(glassfixDef);
			// left
			glassfixDef.shape = new b2PolygonShape;
			glassfixDef.shape.SetAsBox( (glassLW/2)/PTM_RATIO, (glassLH/2)/PTM_RATIO );
			glassBodyDef.position.Set( (glassX+(glassLW/2))/PTM_RATIO, (glassY+(glassLH/2))/PTM_RATIO);
            glassBodyDef.userData = new b2AddOn();
			this.world.CreateBody(glassBodyDef).CreateFixture(glassfixDef);
			// right
			glassfixDef.shape = new b2PolygonShape;
			glassfixDef.shape.SetAsBox( (glassRW/2)/PTM_RATIO, (glassRH/2)/PTM_RATIO );
			glassBodyDef.position.Set( (glassX+(glassRW/2)+glassRXOffset)/PTM_RATIO, (glassY+(glassRH/2))/PTM_RATIO);
            glassBodyDef.userData = new b2AddOn();
			this.world.CreateBody(glassBodyDef).CreateFixture(glassfixDef);
			// Sensor (not box2d sensor, juste AABB use to query at frame time)
			var aabb = new b2AABB();
			var off = 32.0;
			aabb.lowerBound.Set( (glassX/PTM_RATIO)+(off/PTM_RATIO), (glassY/PTM_RATIO)+(off/PTM_RATIO) );
			aabb.upperBound.Set( (glassX/PTM_RATIO)+(glassBW/PTM_RATIO)-(off/PTM_RATIO), (glassY/PTM_RATIO)+(glassSensorH/PTM_RATIO) );
			this.waterSensorAABB = aabb;
	},
	
	update:function (dt) {
		if( this.SelectedObject )
		{	
			var b2M = this.b2MousePosition;
			var b2ObjP = this.SelectedObject.GetPosition();
			var speedCoef = 5.0;
			var dir = new b2Vec2( (b2M.x - b2ObjP.x)*speedCoef , (b2M.y - b2ObjP.y)*speedCoef );
			
			this.SelectedObject.SetLinearVelocity(  dir );
		}
		this.TestBoxInWater();

        var velocityIterations = 8;
        var positionIterations = 1;

        this.world.Step(dt, velocityIterations, positionIterations);

        //Iterate over the bodies in the physics world
        for (var b = this.world.GetBodyList(); b; b = b.GetNext()) {
            if (b.GetUserData() != null && b.GetUserData().cocosData != null) {
                var myActor = b.GetUserData().cocosData;
                myActor.setPosition(cc.p(b.GetPosition().x * PTM_RATIO, b.GetPosition().y * PTM_RATIO));
                myActor.setRotation(-1 * cc.RADIANS_TO_DEGREES(b.GetAngle()));
            }
        }
    },
	
	TestBoxInWater:function(){
		
		var __this = this;
		var QueryResultRemain = [];
		var QueryResultNew = [];
		this.world.QueryAABB( function(fix){
			
			if( fix.GetBody().GetUserData().tag == TagList.Box )
			{
				var b = fix.GetBody();
				
				var Find = false;
				for(var i=0;i<__this.BoxInWaterList.length; i++)
				{
					if( __this.BoxInWaterList[i] == b )
						Find = true;
				}
				
				if(!Find)
					QueryResultNew.push(b);
				else
					QueryResultRemain.push(b);
			}

			return true;
			
		}, this.waterSensorAABB );
		
		
		// remove loop
		for(var i=0;i<__this.BoxInWaterList.length; i++)
		{
			var Find = false;
			for(var j=0;j< QueryResultRemain.length; j++)
			{
				if( this.BoxInWaterList[i] == QueryResultRemain[j] )
					Find = true;
			}
			
			if(!Find)
			{ 
				// Water Effect (Box exit water)
				var contact_point = this.BoxInWaterList[i].GetUserData().cocosData.getPosition();    
				this._water.Height -= 10;
				this.waterSensorAABB.upperBound.y -= (10/PTM_RATIO);
				this._water.addForce(contact_point,200);
			
				// remove a box	
				this.BoxInWaterList[i] = this.BoxInWaterList[ this.BoxInWaterList.length - 1 ];
				this.BoxInWaterList.pop();
				i--;
			}
		}
		
		// add loop
		for(var i=0;i< QueryResultNew.length; i++)
		{
			// Water Effect (Box enter in water)
			var contact_point = QueryResultNew[i].GetUserData().cocosData.getPosition();    
            this._water.Height += 10;
			this.waterSensorAABB.upperBound.y += (10/PTM_RATIO);
            this._water.addForce(contact_point,-200);
			
			this.BoxInWaterList.push( QueryResultNew[i] );
		}
		
		console.log(this.BoxInWaterList.length);
	},
	// inputs 
	onTouchesBegan:function (pTouch, event) {
		if( pTouch[0] )
		{
			var localTouch = this.convertTouchToNodeSpace(pTouch[0]);
			var worldTouch = this.convertToWorldSpace(localTouch);
			var b2WorldPos = transformToB2World(worldTouch);
			this.b2MousePosition = b2WorldPos;
			
			var __this = this;
			this.world.QueryPoint( function(fix){
				if( fix.GetBody().GetUserData().selectable )
				{
					__this.SelectedObject = fix.GetBody();
					return false;
				}
				else
				{
					return true;
				}
			}, b2WorldPos );
		}
    },
    onTouchesMoved:function(pTouch,pEvent){
		// Box2D selectable object
		if( pTouch[0] )
		{
			var localTouch = this.convertTouchToNodeSpace(pTouch[0]);
			var worldTouch = this.convertToWorldSpace(localTouch);
			worldTouch = cc.Director.getInstance().convertToGL(worldTouch);
			var b2WorldPos = new b2Vec2( worldTouch.x / PTM_RATIO, worldTouch.y / PTM_RATIO );
			
			this.b2MousePosition = b2WorldPos;
			
			if( this.SelectedObject )
			{	
				//this.SelectedObject.SetPosition( b2WorldPos/*, this.SelectedObject.GetAngle() */);
				//this.SelectedObject.SetLinearVelocity(  new b2Vec2(0,0) );
			}
		}
	},
	onTouchesEnded:function (pTouch,pEvent){
       if( pTouch[0] )
	   {
		   // water effects
		   this._water.handleTouch( pTouch[0] );
		}
	   
	   // remove selection
	   this.SelectedObject = null;
    },
    onKeyUp:function(e){
    },
    onKeyDown:function(e){
    },
});


var waterScene = cc.Scene.extend({
    onEnter:function () {
        this._super();
        var layer = new _waterScene();
        layer.init();
        this.addChild(layer);
    }
});


